#ifndef DSPSIMULATOR_DSPINSTRUCTION_H
#define DSPSIMULATOR_DSPINSTRUCTION_H

#include <bitset>
#include <utility>
#include <unordered_set>
#include <functional>
#include "simulator/type/type.h"
#include "simulator/bitTools/bitTools.h"
#include "simulator/instruction/instruction.h"
#include "simulator/targetMachine/registerMap.h"
#include "dspSimulator/targetMachine/funcUnitInfo.h"
#include "dspSimulator/targetMachine/dspRegisterInfo.h"
#include "dspSimulator/targetMachine/dspMachine.h"
#include "dspSimulator/targetMachine/addressMode.h"
#include "dspSimulator/targetMachine/dspInstInfo.h"
#include "dspSimulator/irGenerator/irGenerator.h"
#include "dspSimulator/operand/operand.h"
#include "dspSimulator/operand/immediate32.h"
#include "dspSimulator/operand/singleReg.h"
#include "dspSimulator/operand/doubleReg.h"
#include "dspSimulator/operand/fourReg.h"
#include "dspSimulator/operand/registerKind.h"

class DSPSimulator;

class DspInstruction : public Instruction {
private:
    friend class DSPSimulator;
protected:
    using RegCopy = std::unordered_map<std::string, llvm::Value *>;
protected:
    std::shared_ptr<Operand> m_op1;         // operand 1
    std::shared_ptr<Operand> m_op2;         // operand 2
    std::shared_ptr<Operand> m_op3;         // operand 3
    std::shared_ptr<Operand> m_op4;         // operand 4

    bool isCompactInstruction{false};       // 标识是否当前指令为Compact指令

    FuncUnit funcUnit{FuncUnit::UNKNOWN_FUNC};  // 标识当前指令的函数单元

    std::string compactInstKind;    // 标识当前compact指令的格式类型

    std::bitset<4> creg_z;           // standard instruction condition

    std::bitset<2> CC;              // compact instruction condition

    u32 numOfInsertNOP{0};  // 插入execute packet之后的nop指令的数量

    bool p{false};                // parallel execution; 0 = next instruction is not executed in parallel, 1 = next instruction is executed in parallel
public:
    /**
     * @param name 指令名
     * @param baseCycle 当前指令执行E1阶段的时钟周期
     * @param instructionAddress 指令地址
     * @param delay 延时大小
     */
    DspInstruction(const std::string &name, u32 address, u64 baseCycle, u64 delay) :
            Instruction(name, static_cast<u64>(address), baseCycle, delay) {};

    ~DspInstruction() override = default;

    DspInstruction(const DspInstruction &dspInstruction) = default;


    virtual void generateIR(std::shared_ptr<IRGenerator> irGenerator, llvm::Function *function, RegCopy &regCopy) = 0;

protected:
    void generateIRWithCondition(std::shared_ptr<IRGenerator> irGenerator,llvm::Function *function, RegCopy &regCopy);

    /**
     * 判断Standard指令是否存在条件字段
    * @return 如果Standard指令存在条件字段，返回true，否则返回false
     */
    static bool existStandardCondition(std::bitset<32> data);

    /**
     * 获取src1标识的32位寄存器类型
     * @return src1标识的32位寄存器类型
     */
    RegKind getSrc1Reg32Kind(u32 src1, bool s) const;

    /**
     * 获取src1标识的64位寄存器名称
     * @return src1标识的64位寄存器名称
     */
    std::string getSrc1Reg64Name() const;

    /**
     * 获取src2标识的32位寄存器类型
     * @return src2标识的32位寄存器类型
     */
    RegKind getSrc2Reg32Kind(u32 src2, bool s, bool x = false) const;

    /**
     * 获取src2标识的64位寄存器名称
     * @return src2标识的64位寄存器名称
     */
    std::string getSrc2Reg64Name() const;

    /**
     * 获取dst标识的32位寄存器类型
     * @return dst标识的32位寄存器类型
     */
    RegKind getDstReg32Kind(u32 dst, bool s) const;

    /**
     * 获取dst标识的64位寄存器名称
     * @return dst标识的64位寄存器名称
     */
    std::string getDstReg64Name() const;

    /**
     * 根据Crhi和CrLo字段获取控制寄存器的类型
     * @return 控制寄存器的类型
     */
    CRegKind getCRegKindByCrHiAndCrLo() const;

    /**
     * 根据BaseR字段获取基址寄存器名
     * @return 基址寄存器名
     */
    std::string getBaseRRegName() const;

    /**
     * 根据offsetR字段获取变指寄存器名
     * @return 变指寄存器名
     */
    std::string getOffsetRRegName() const;

    /**
    * 根据寄存器寻址模式计算地址的实际偏移量
    * @param offset 输入的偏移量
    * @param mode 寄存器寻址模式
    */
    static void fixUpAddressOffset(u32 &offset, AddressingMode mode, std::shared_ptr<DspMachine> dspMachine);

    /**
    * 根据指令寻址模式计算地址的实际地址
    * @param baseRPtr 寻址的基址寄存器
    * @param mode  指令寻址的模式
    * @param leftshift 地址偏移量
    * @param dspMachine
    * @return 寻址访问内存的真实地址
    */
    u32 computeRealAddress(const u32 *baseRReadPtr, u32 *baseRWritePtr, u32 addressingMethod, u32 leftshift,
                           std::shared_ptr<DspMachine> dspMachine);

    /**
     * 取指包的RS位会影响Compact指令的寄存器的使用类型，通过该函数修正Compact指令，使寄存器表示为正式的数
     */
    void fixUpRegOnCompactInstruction();

    /**
     * 获取寻址指令的寻址模式字符串
     * @return 指令的寻址模式字符串
     */
    std::string addressModeToString() const;

};

#endif //DSPSIMULATOR_DSPINSTRUCTION_H
